refactor: modularize vite.config.ts and fix Windows script compatibility#379
refactor: modularize vite.config.ts and fix Windows script compatibility#379facusturla wants to merge 1267 commits intokoala73:mainfrom
Conversation
- Use for...of entries() instead of index-based loops in summarization.ts to satisfy strict noUncheckedIndexedAccess (7 TS18048/TS2345 errors) - Replace fragile API_PROVIDERS[1] with .find(p => p.name === groq) - Add OLLAMA_API_URL and OLLAMA_MODEL to SUPPORTED_SECRET_KEYS in main.rs so keychain secrets are injected into sidecar on desktop startup
…and Ollama UX - Split settings window into 3 tabs: LLMs (Ollama/Groq/OpenRouter), API Keys (data feeds), and Debug & Logs - Add featureFilter option to RuntimeConfigPanel for rendering subsets - Consolidate keychain to single JSON vault entry (1 macOS prompt vs 20) - Add Ollama model discovery with /api/tags + /v1/models fallback - Strip <think> reasoning tokens from Ollama responses - Suppress thinking with think:false in Ollama request body - Parallel secret verification with 15s global timeout - Fix manual model input overlapping dropdown (CSS grid-area + hidden-input class) - Add loading spinners to settings tab panels - Suppress notification popups when settings window is open - Filter embed models from Ollama dropdown - Fix settings window black screen flash with inline dark background
Add local LLM support mentions across feature descriptions, talking points, screenshot suggestions, and changelog. New dedicated section for Ollama/LM Studio as feature koala73#11.
DeckGLMap: guard updateLayers/debounce/raf against null maplibreMap, null out references in destroy() to prevent post-destroy setProps crash. main.ts: filter contentWindow.postMessage (Facebook WebView), vertex shader compile (GPU driver), objectStoreNames (iOS background), Unexpected identifier https (Safari 16), _0x obfuscated vars (extensions), WKWebView deallocated (Tauri lifecycle).
…koala73#124) ## Summary Extracts shared summarization logic (CORS, validation, caching, prompt building) into a reusable `_summarize-handler.js` factory, then uses it to add Ollama as the first provider in the fallback chain. This reduces code duplication across Groq, OpenRouter, and the new Ollama endpoint while maintaining identical behavior. **Fallback chain is now:** Ollama → Groq → OpenRouter → Browser T5 ## Type of change - [x] New feature - [x] Refactor / code cleanup - [x] API endpoints (`/api/*`) ## Affected areas - [x] AI Insights / World Brief - [x] Desktop app (Tauri) - [x] API endpoints (`/api/*`) - [x] Config / Settings ## Changes ### New Files - **`api/_summarize-handler.js`** – Shared handler factory with: - `createSummarizeHandler(providerConfig)` – Creates edge handlers for any LLM provider - `getCacheKey()` – Stable cache key generation (extracted from per-provider code) - `deduplicateHeadlines()` – Headline deduplication logic (extracted from per-provider code) - Unified prompt building for brief/analysis/translate modes with tech/full variants - CORS, validation, caching, and error handling pipeline - **`api/ollama-summarize.js`** – New Ollama endpoint (34 lines): - Calls local/remote Ollama instance via OpenAI-compatible `/v1/chat/completions` - Reads `OLLAMA_API_URL` and `OLLAMA_MODEL` from environment - Shares Redis cache with Groq/OpenRouter (same cache key strategy) - Returns fallback signal when unconfigured or API fails - **`api/ollama-summarize.test.mjs`** – Unit tests for Ollama endpoint: - Fallback signal when `OLLAMA_API_URL` not configured - Success response with provider="ollama" - Error handling (API errors, empty responses) - Model selection via `OLLAMA_MODEL` env - **`api/_summarize-handler.test.mjs`** – Unit tests for shared factory: - Cache key stability and variation by mode/variant/lang/geoContext - Headline deduplication logic - Handler creation with missing credentials - API provider calls and response shaping - **`tests/summarization-chain.test.mjs`** – Integration tests for fallback chain: - Ollama success short-circuits (no downstream calls) - Ollama failure → Groq success - Both fail → fallback signals propagate ### Modified Files **`api/groq-summarize.js`** & **`api/openrouter-summarize.js`** - Replaced ~290 lines of duplicated logic with single call to `createSummarizeHandler()` - Now thin wrappers: 26 lines (Groq) and 28 lines (OpenRouter) - Identical behavior, zero functional changes **`src/services/summarization.ts`** - Updated fallback chain: `Ollama → Groq → OpenRouter → Browser T5` - Refactored `tryGroq()` and `tryOpenRouter()` into unified `tryApiProvider()` function - Added `API_PROVIDERS` config array for provider ordering - Updated `SummarizationProvider` type to include `'ollama'` **`src-tauri/sidecar/local-api-server.mjs`** - Added `OLLAMA_API_URL` and `OLLAMA_MODEL` to `ALLOWED_ENV_KEYS` - Added validation for `OLLAMA_API_URL` in `validateSecretAgainstProvider()`: - Probes `/v1/models` (OpenAI-compatible endpoint) - Falls back to `/api/tags` (native Ollama endpoint) - Returns "Ollama endpoint verified" or "Ollama endpoint verified (native API
…buffers Toggling cables/pipelines off then on caused deck.gl assertion failure because the cached PathLayer had its WebGL resources destroyed on removal.
…USNI News, Oryx OSINT, UK MOD)
…keychain vault - Ollama/LM Studio integration with auto model discovery and 4-tier fallback chain - Settings window split into LLMs, API Keys, and Debug tabs - Consolidated keychain vault (1 OS prompt instead of 20+) - README expanded with privacy architecture, summarization chain docs - CHANGELOG updated with full v2.5.0 release notes - 5 new defense/intel RSS feeds, Koeberg nuclear plant added
Removes circular dev→prod dependency. The new polymarketPlugin() mirrors the edge function logic locally: validates params, fetches from gamma-api.polymarket.com, and gracefully returns [] when Cloudflare JA3 blocks server-side TLS (expected behavior).
The Vite dev plugin was hardcoding `{ videoId: null }` with a TODO,
causing LiveNewsPanel to never resolve actual live streams during local
development. Replace the stub with the same fetch-and-scrape approach
used by the production edge function (api/youtube/live.js): fetch the
channel's /live page and extract videoId + isLive from the HTML.
https://claude.ai/code/session_01684qa7XvS7sf9CShqU8zNg
Store the interval ID returned by setInterval in a new clockIntervalId field and clear it in App.destroy(). Previously, the interval was never stored or cleared, causing DOM writes to double on each Vite HMR reload. https://claude.ai/code/session_0111CXxXM5qKR83UAdUTQDyL
npm install regenerated the lockfile to reflect the current version (2.5.0) and license field from package.json. https://claude.ai/code/session_01684qa7XvS7sf9CShqU8zNg
…eived load Inline a minimal HTML skeleton (header bar, map placeholder, panels grid) with critical CSS directly in index.html so the page shows a structured layout immediately instead of a blank screen while JavaScript boots. - Dark theme skeleton with hardcoded colors matching CSS variables - Light theme support via [data-theme="light"] selectors - Shimmer animation on placeholder lines for visual feedback - 6 skeleton panels in a responsive grid matching the real layout - Map section with radial gradient matching the map background - Skeleton is automatically replaced when App.renderLayout() sets innerHTML - Marked aria-hidden="true" for screen reader accessibility Expected gain: perceived load time drops to <0.5s. https://claude.ai/code/session_01Fxk8GMRn2cEUq3ThC2a8e5
Replace the static LOCALE_LOADERS map (14 explicit dynamic imports) with import.meta.glob for lazy loading. English is now statically imported as the always-needed fallback; all other 13 locales are loaded on demand. - Statically import en.json so it's bundled eagerly (no extra fetch) - Use import.meta.glob with negative pattern to lazy-load non-English locales only when the user actually switches language - Add manualChunks rule to prefix lazy locale chunks with `locale-` - Exclude locale-*.js from service worker precache via globIgnores - Add CacheFirst runtime caching for locale files when loaded on demand SW precache reduced from 43 entries (5587 KiB) to 29 entries (4840 KiB), saving ~747 KiB from the initial download. https://claude.ai/code/session_01TfRgC5GWsv51swxRSGxxeJ
Audit and fix localization coverage gaps across 12 components that were using hardcoded English strings instead of the i18next t() system. Components fixed: - IntelligenceGapBadge.ts: context menu label - InvestmentsPanel.ts: filters, table headers, sector labels, statuses - Panel.ts: aria-label, resize tooltip, settings button - LanguageSelector.ts: aria-label - ServiceStatusPanel.ts: loading state, category filters, status labels - TechEventsPanel.ts: tab labels, stats, badges - StrategicRiskPanel.ts: risk metrics, section titles, time formatting - RegulationPanel.ts: dashboard title, tabs, section headers, stances - TechReadinessPanel.ts: fetching state indicators, source attribution - InsightsPanel.ts: progress steps, empty states - VerificationChecklist.ts: all UI labels and verdict text - LiveNewsPanel.ts: offline/error messages Added ~150 new translation keys to en.json and propagated them as English placeholders to all 13 other locale files. https://claude.ai/code/session_018UKmgomYsVsEmJfX7Ava3v
Convert VERIFICATION_TEMPLATE module-level constant to getVerificationTemplate() function to defer t() calls, and add 8 new i18n keys for checklist item labels. https://claude.ai/code/session_018UKmgomYsVsEmJfX7Ava3v
Add contain: content to the .panel class so the browser knows that layout changes inside a panel don't affect siblings. The .panel-content class already has contain: layout style for scroll performance. Expected gain: faster layout recalculations during panel updates. https://claude.ai/code/session_01E9FpgiebjEuUPhNt8mwX9U
Add will-change: transform, opacity to dragged panels, signal modal, and mobile warning modal. Add will-change: transform to map markers and map popup sheet. Remove will-change via animationend/transitionend listeners after one-shot animations complete to free GPU memory. https://claude.ai/code/session_01DDhT6Ex596eX1CtSb6mHdH
Fix ignoreErrors regex for unsafe-eval CSP violations — word order didn't match actual error messages. Add filter for "Unexpected end of input" from truncated WebView script loads.
Fixes: - Linux AppImage black screen on WebKit/GStreamer (koala73#411) - Destroy live news player before showing offline/error message (koala73#410)
Launch the built AppImage under Xvfb after the Linux build to catch startup crashes and render failures automatically. Uploads a screenshot artifact for visual inspection.
…oala73#414) The squash merge of koala73#413 put smoke test steps into build-desktop.yml instead of the intended standalone workflow. This commit: - Reverts build-desktop.yml to its original state - Adds test-linux-app.yml as a separate Linux-only smoke test workflow
545bd31 to
66b8095
Compare
|
Thanks for the thorough review! I've addressed all the feedback: BLOCKING fixes:
SUGGESTION fixes: Converted all vite/*.ts files from 4-space to 2-space to match project convention. Exported a VariantMeta interface from variants.tsand imported it in plugins.ts. Replaced double cross-env calls with cross-env-shell for all chained scripts. Verified locally: npm run build passes (tsc + vite build in ~40s). |
* fix(ci): use weston+XWayland for Linux smoke test instead of pure Wayland Previous attempt used GDK_BACKEND=wayland which caused GTK init panic (tao requires X11). Now: weston headless with XWayland provides X11 through a real compositor. Falls back to Xvfb if weston fails. Also uploads weston/app logs as artifacts for debugging. * refactor(ci): use xwfb-run for Linux smoke test display xwfb-run (from xwayland-run package) is purpose-built for this: Xwayland on a headless Wayland compositor, replaces xvfb-run. Falls back to plain Xvfb if xwfb-run is unavailable. Uploads display-server and app logs as artifacts.
Only run CI on PRs from branches within the repo, not from external forks. Prevents unnecessary action minutes from contributor PRs.
* ci(linux): add AppImage smoke test to desktop build Launch the built AppImage under Xvfb after the Linux build to catch startup crashes and render failures automatically. Uploads a screenshot artifact for visual inspection. * Optimize Wingbits API usage and panel polling
…oala73#419) Linux had no keyring backend feature enabled — keyring v3 fell back to in-memory mock store. Secrets appeared to save but were lost on restart. Added `linux-native-sync-persistent` (kernel keyutils + D-Bus Secret Service combo) and `crypto-rust` for Secret Service encryption. This uses GNOME Keyring or KDE Wallet on desktop Linux for persistent storage.
…ala73#424) * chore: bump v2.5.12 ## Changelog - fix(linux): enable keyring persistence via Secret Service + keyutils (koala73#419) - fix(ci): use weston+XWayland for Linux smoke test (koala73#417) - ci: add standalone Test Linux App workflow (koala73#414) - ci: skip Typecheck and Lint on fork PRs (koala73#415) - perf: optimize Wingbits API usage and reduce unnecessary polling (koala73#416) * fix(linux): append host GStreamer plugins to AppImage search path The linuxdeploy GStreamer hook force-overrides GST_PLUGIN_PATH_1_0 and GST_PLUGIN_SYSTEM_PATH_1_0 to only contain bundled plugins from the CI build system (Ubuntu 24.04, GStreamer 1.24). On hosts with newer GStreamer (e.g. Arch 1.28), codec plugins like gst-libav and fakevideosink from gst-plugins-bad are invisible — WebKit can't play video. Append common host GStreamer plugin directories as fallback so the system's codec plugins are discoverable while bundled plugins retain priority. Also fixes: - tauri.conf.json devUrl port mismatch (5173 → 3000) breaking desktop:dev - live-channels-window YouTube validation allowing add on non-OK responses
…channels (koala73#425) The /api/youtube/live validation endpoint may return 429 or non-JSON responses (Vercel WAF, YouTube rate limiting). Previously this caused res.json() to parse HTML → either throw (caught, channel added) or return channelExists:false (blocked add with red border). Now only blocks when the API explicitly returns 200 OK with channelExists:false — any non-OK status or error allows the add. Also bumps version to 2.5.13.
- CI: add ubuntu-24.04-arm matrix entry with aarch64-unknown-linux-gnu - Node sidecar: download linux-arm64 Node.js bundle for ARM runners - Download API: add linux-appimage-arm64 pattern for aarch64 AppImage - Download banner: show both x64 and ARM64 Linux options (UA can't distinguish) - Desktop updater: map Linux aarch64 to linux-appimage-arm64 for in-app updates - Smoke test: fix AppImage search path for cross-target builds
Changes since v2.5.13: - feat: add ARM64 Linux build target and download detection (koala73#427) - fix(live-channels): tolerate YouTube API failures when adding custom channels (koala73#425) - fix(linux): append host GStreamer plugins to AppImage search path (koala73#424) - fix(linux): enable keyring persistence via Secret Service + keyutils (koala73#419)
…rrors (koala73#429) - Widen beforeSend regex to catch `null is not an object (evaluating 'u.id')` pattern from deck.gl internals during variant switch (WORLDMONITOR-4A, 270 events) - Remove `in_app` requirement from TypeError suppression — Sentry SDK marks deck.gl/maplibre frames inconsistently, causing the filter to miss - Fix Firefox lexical declaration wording: `can't access` vs Chrome's `Cannot access` - Add noise filters: isReCreate (Android WebView injection), HTMLImageElement style access, WebGL context loss write access
…3#430) * fix(sentry): tighten noise filters for deck.gl/maplibre and WebView errors - Widen beforeSend regex to catch `null is not an object (evaluating 'u.id')` pattern from deck.gl internals during variant switch (WORLDMONITOR-4A, 270 events) - Remove `in_app` requirement from TypeError suppression — Sentry SDK marks deck.gl/maplibre frames inconsistently, causing the filter to miss - Fix Firefox lexical declaration wording: `can't access` vs Chrome's `Cannot access` - Add noise filters: isReCreate (Android WebView injection), HTMLImageElement style access, WebGL context loss write access * fix: reduce upstream API pressure with cache TTL optimization - Military/posture: 5min → 15min (flight cache, theater posture, panel refresh, intelligence refresh) - Theater posture: fetch 2 targeted bbox regions instead of global states/all (~95% less data) - Wingbits batch: reduce from 20 to 10, sequential with 100ms delay instead of Promise.all burst - Preserve intelligenceCache.military across intelligence refresh cycles - OpenSky edge proxy: add CDN caching (s-maxage=120), align timeout to 20s - list-military-flights: Redis cache 2min → 10min - Market handlers: stablecoins/crypto/commodities/sectors 3min → 5min - Cable health: 3min → 10min - YouTube embed: s-maxage 60s → 15min
…oala73#431) Both ubuntu-24.04 (x64) and ubuntu-24.04-arm (ARM64) upload a smoke test screenshot with the static name `linux-smoke-test-screenshot`. upload-artifact@v4 rejects duplicate names with 409 Conflict. Append matrix.label so each gets a unique artifact name. CI log proof: run 22452393753 shows the ARM64 "Upload smoke test screenshot" step failing on the exact artifact name collision.
…oala73#434) AppImage only bundled gst-plugins-base and gst-plugins-good, missing H.264/AAC (gst-libav), x264 (plugins-ugly), AV1 (plugins-bad), and GL video sink (gst-gl). YouTube's MSE player checks codec support via MediaSource.isTypeSupported() — WebKitGTK delegates to GStreamer and reports no compatible decoders, showing "can't play this video". Add plugins-bad, plugins-ugly, gst-libav, and gst-gl to CI install so bundleMediaFramework includes them in the AppImage.
…ala73#435) * fix(linux): bundle full GStreamer codec suite for YouTube playback AppImage only bundled gst-plugins-base and gst-plugins-good, missing H.264/AAC (gst-libav), x264 (plugins-ugly), AV1 (plugins-bad), and GL video sink (gst-gl). YouTube's MSE player checks codec support via MediaSource.isTypeSupported() — WebKitGTK delegates to GStreamer and reports no compatible decoders, showing "can't play this video". Add plugins-bad, plugins-ugly, gst-libav, and gst-gl to CI install so bundleMediaFramework includes them in the AppImage. * feat: support YouTube URLs in custom channels, add Middle East region - Parse YouTube watch URLs and channel URLs in the custom channel input (watch?v=, youtu.be/, @handle URLs all supported) - Auto-resolve channel/video names via YouTube oembed proxy - Add video ID lookup endpoint to api/youtube/live.js - Return channelName from channel live detection API - Add Middle East region tab (Al Hadath, Sky News Arabia, TRT World, Iran International, CGTN Arabic) - Add BBC News and France 24 English to Europe region - Rename NASA TV to Sen Space Live - Add i18n keys (youtubeHandleOrUrl, regionMiddleEast) to all 17 locales
koala73
left a comment
There was a problem hiding this comment.
PR Review: Modularize vite.config.ts + Windows script compatibility
Overall
Good direction — splitting a 690-line config into focused modules improves readability. cross-env for Windows compat is the right tool. Several issues need addressing before merge.
BLOCKING
1. RSS_PROXY_ALLOWED_DOMAINS in vite/plugins.ts is ~62 domains behind api/rss-proxy.js
The dev-server RSS proxy plugin has its own hardcoded domain allowlist that has drifted massively from the production source of truth (api/rss-proxy.js). Missing domains include all euronews locales, Indian/Greek/LatAm/African regional sources, many international news outlets added over recent months, and happy-variant sources like dailygood.org, goodgoodgood.co, etc.
Also adds www.huffpost.com which doesn't exist in production.
This creates a situation where feeds work in production but fail silently in dev (403 from the proxy), making debugging painful.
Suggested fix: Import the domain list from a shared constant, or — simpler — just import/re-use from api/rss-proxy.js. Having two copies guarantees drift.
2. sebufApiPlugin path resolution changed from relative imports to resolve(serverRoot, ...)
Current code uses:
import('./server/router')
import('./src/generated/server/worldmonitor/seismology/v1/service_server')PR changes to:
import(resolve(serverRoot, 'server/router'))
import(resolve(serverRoot, 'src/generated/server/worldmonitor/seismology/v1/service_server'))Where serverRoot = fileURLToPath(new URL('.', import.meta.url)) which resolves to the project root. These should be equivalent, but dynamic import() with resolve() paths bypasses Vite's module graph — meaning HMR invalidation of server modules may not work correctly. The existing file watcher (server.watcher.on('change', ...)) nullifies cachedRouter, but Vite won't track the import dependencies. This is a functional regression risk for dev experience.
3. __dirname polyfill is unnecessary and subtly wrong
const __dirname = fileURLToPath(new URL('.', import.meta.url));Vite already provides __dirname in config files via its CJS compatibility layer. The existing code on main uses __dirname directly without any polyfill (lines 815-845). This shadows the built-in with a manually constructed version. While the value should be identical, it's unnecessary complexity.
SUGGESTIONS
4. cross-env-shell quoting may break on some Windows shells
Scripts like:
"build:full": "cross-env-shell VITE_VARIANT=full \"tsc && vite build\""The nested escaped quotes work in most cases but are fragile with some Windows terminal emulators. Consider testing on actual Windows (cmd.exe, PowerShell, Git Bash).
5. Missing newline at end of package.json
The diff shows \ No newline at end of file — the trailing newline was removed. This causes unnecessary git noise on the next edit.
6. Comments stripped from vite.config.ts
Several useful comments were removed during the refactor (e.g., "Geospatial bundles are expected to be large", "onnxruntime-web ships a minified browser bundle", "Give lazy-loaded locale chunks a recognizable prefix"). These explain non-obvious decisions. The manualChunks one-liners are less readable without them.
7. process.env.FRED_API_KEY in vite/proxy.ts leaks server env into proxy config
The FRED proxy rewrite function reads process.env.FRED_API_KEY directly. This works but means the proxy config function is impure (depends on env state at call time). Consider passing it as a parameter or documenting the dependency.
Summary
The modularization structure is good. The main blocker is the RSS domain list drift (62+ missing domains) — this will cause dev/prod parity issues. The sebufApiPlugin path resolution change needs testing to confirm HMR still works. Fix those two and this is ready to merge.
Add X-Content-Type-Options, X-Frame-Options, Strict-Transport-Security, Content-Security-Policy, Referrer-Policy, and Permissions-Policy headers to all routes via vercel.json catch-all pattern.
- Indian Express (India section) → asia region - India News Network (diplomacy) → asia region - The War Zone → updated to direct feed (was Google News proxy) - gCaptain (maritime/waterways) → intel sources - Added domains to RSS proxy allowlist
…malize indentation, type activeMeta, use cross-env-shell
…ths, remove __dirname polyfill - Extract RSS allowed domains into shared api/rss-allowed-domains.js (single source of truth for both prod and dev proxy) - Revert sebufApiPlugin to relative import() paths for Vite module graph compat - Remove unnecessary __dirname polyfill (Vite provides it natively) - Restore explanatory comments in manualChunks/onwarn - Document FRED_API_KEY env dependency in proxy.ts - Restore trailing newline in package.json
5ae50dd to
635722f
Compare
Addresses PR review feedbackBlocking fixes1. RSS domain drift resolved — Created Suggestions addressed4. cross-env-shell quoting — The nested escaped quotes
Rebase noteRebased onto latest |
This PR addresses two maintenance and developer experience issues:
package.jsonwithcross-env.npm run dev:techandnpm run build:financework correctly on Windows PowerShell/CMD.vite.config.tsinto a modular structure under a newvite/directory.vite/variants.ts: Site variant metadata.vite/proxy.ts: Proxy rules for APIs and RSS feeds.vite/plugins.ts: Custom Vite plugins (Sebuf, RSS proxy, etc.).Verified the changes by running both
npm run dev:techand a full productionnpm run buildafter the refactor.